home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue24 / ntserv / DemoSv1.Dpr < prev    next >
Encoding:
Text File  |  1997-07-04  |  12.8 KB  |  356 lines

  1. program DemoSv1;
  2.  
  3. {==============================================================================}
  4. { NT service which demonstrates the typical flow sequence for a service. Does  }
  5. { not use any Delphi classes so we can concentrate on the interaction with the }
  6. { Service control manager.                                                     }
  7. {                                                                              }
  8. { This was written by John Chaytor and accompanies an aticle in 'The Delphi    }
  9. { Magazine'. See that article for a detailed discussion of NT services.        }
  10. {==============================================================================}
  11.  
  12. Uses
  13.   Windows, SysUtils, Registry, WinSvcX, Demo1Log, Logging;
  14.  
  15. const
  16.   DemoServiceName        = 'DemoService1';
  17.   DemoServiceDisplayName = 'Demonstation service 1';
  18.   EventRegKey            = 'SYSTEM\CurrentControlSet\Services\EventLog\Application\';
  19.  
  20. var
  21.   FTerminated: Boolean;
  22.   FPauseStartTicks: LongInt;
  23.   FServiceStatus: TServiceStatus;
  24.   FServicStatusHandle: SERVICE_STATUS_HANDLE;
  25.  
  26. type
  27.   PCharArray = ^TPCharArray;
  28.   TPCharArray = array[0..0] of PChar;
  29.  
  30. {------------------------------------------------------------------------------}
  31. procedure DisplaySyntaxOptions;
  32. begin
  33.   WriteLn('');
  34.   WriteLn('Command syntax options :-');
  35.   WriteLn('');
  36.   WriteLn('DemoSv1 INSTALL');
  37.   WriteLn('DemoSv1 I');
  38.   WriteLn('DemoSv1 UNINSTALL');
  39.   WriteLn('DemoSv1 U');
  40.   WriteLn('DemoSv1 VERSION');
  41.   WriteLn('DemoSv1 V');
  42.   WriteLn('');
  43. end;
  44. {------------------------------------------------------------------------------}
  45. procedure LogEvent(Severity: DWord; Id: DWord; Inserts: PCharArray; NumInserts: Integer);
  46. var
  47.   EventSource: THandle;
  48.  
  49. begin
  50.   EventSource := RegisterEventSource(nil,DemoServiceName);
  51.   try
  52.     ReportEvent(EventSource,Severity,0,Id,nil,NumInserts,Sizeof(FServiceStatus),PChar(Inserts[0]),@FServiceStatus);
  53.   finally
  54.     DeRegisterEventSource(EventSource);
  55.   end;
  56. end;
  57. {------------------------------------------------------------------------------}
  58. procedure InstallService;
  59. Var
  60.   hSCManager: SC_Handle;
  61.   hService: SC_Handle;
  62.  
  63.   procedure AddEventDetailsToRegistry;
  64.   var
  65.     EventKey: String;
  66.  
  67.   begin
  68.     EventKey := Format('%s%s',[EventRegKey,DemoServiceName]);
  69.     With TRegistry.Create do
  70.      try
  71.        RootKey := HKEY_LOCAL_MACHINE;
  72.        if OpenKey(EventKey,True) then
  73.          try
  74.            WriteInteger('TypesSupported',EVENTLOG_ERROR_TYPE or EVENTLOG_WARNING_TYPE or EVENTLOG_INFORMATION_TYPE);
  75.            WriteString('EventMessageFile',ParamStr(0));
  76.            WriteLn('Registry has been updated for event logging.');
  77.          except
  78.            DeleteKey(EventKey);
  79.            Raise;
  80.          end
  81.        else
  82.          WriteLn(Format('Failed to open key %s',[EventKey]));
  83.      finally
  84.        Free;
  85.      end;
  86.   end;
  87.  
  88. Begin
  89.   WriteLn('Installing service...');
  90.   WriteLn(Format('Path to service module is %s',[ParamStr(0)]));
  91.   hSCManager:= OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
  92.   If hSCManager <> 0 then
  93.     try
  94.       hService:= CreateService(hSCManager,DemoServiceName,DemoServiceDisplayName,
  95.                                  SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,
  96.                                  SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,
  97.                                  PChar(ParamStr(0)),nil,nil,nil,nil,nil);
  98.       if hService <> 0 then
  99.         begin
  100.           WriteLn('Service was installed successfully.');
  101.           AddEventDetailsToRegistry;
  102.         end
  103.       else
  104.         WriteLn(Format('Failed to create the service. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  105.     finally
  106.       CloseServiceHandle(hSCManager)
  107.     end
  108.   else
  109.     WriteLn(Format('Failed to open Service Control Manager. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  110. End;
  111.  
  112. {------------------------------------------------------------------------------}
  113. procedure UninstallService;
  114. Var
  115.   hSCManager: SC_Handle;
  116.   hService: SC_Handle;
  117.  
  118.   procedure RemoveEventDetailsFromRegistry;
  119.   var
  120.     EventKey: String;
  121.  
  122.   begin
  123.     if DemoServiceName <> '' then
  124.       begin
  125.         EventKey := Format('%s%s',[EventRegKey,DemoServiceName]);
  126.         With TRegistry.Create do
  127.          try
  128.            RootKey := HKEY_LOCAL_MACHINE;
  129.            DeleteKey(EventKey);
  130.            WriteLn('Registry details for event logging has been removed.');
  131.          finally
  132.            Free;
  133.          end;
  134.       end
  135.     else
  136.       WriteLn('Service name missing! Registry not updated.');
  137.   end;
  138.  
  139. Begin
  140.   hSCManager := OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
  141.   If hSCManager <> 0 then
  142.     try
  143.       hService := OpenService(hSCManager,DemoServiceName,SERVICE_ALL_ACCESS);
  144.       if hService <> 0 then
  145.         try
  146.           if DeleteService(hService) then
  147.             begin
  148.               WriteLn('Service was uninstalled successfully.');
  149.               RemoveEventDetailsFromRegistry;
  150.             end
  151.           else
  152.             WriteLn(Format('Failed to delete service. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  153.         finally
  154.           CloseServiceHandle(hService);
  155.         end
  156.       else
  157.         WriteLn(Format('Failed to open service "%s": Error was ''%s''',[DemoServiceName,SysErrorMessage(GetLastError)]));
  158.     finally
  159.       CloseServiceHandle(hSCManager)
  160.     end
  161.   else
  162.     WriteLn(Format('Failed to open Service control Manager. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  163. End;
  164. {------------------------------------------------------------------------------}
  165. procedure DisplayVersion;
  166. begin
  167.   WriteLn('DemoSv1 version 1.00');
  168. end;
  169. {------------------------------------------------------------------------------}
  170. procedure DemoServiceHandler(Code: Integer); StdCall;
  171. var
  172.   Inserts: array[0..0] of PChar;
  173.   Text: array[0..255] of char;
  174.  
  175. begin
  176.   case code of
  177.     SERVICE_CONTROL_STOP:
  178.       begin
  179.         With FServiceStatus do
  180.           begin
  181.             dwCurrentState := SERVICE_STOP_PENDING;
  182.             dwWin32ExitCode := 0;
  183.             dwServiceSpecificExitCode := 0;
  184.             dwCheckPoint := 0;
  185.             dwWaitHint := 0;
  186.           end;
  187.       end;
  188.     SERVICE_CONTROL_PAUSE:
  189.       begin
  190.         FPauseStartTicks := GetTickCount;
  191.         FServiceStatus.dwCurrentState := SERVICE_PAUSED;
  192.         LogEvent(EVENTLOG_WARNING_TYPE,DEMO1_SERVICE_PAUSED,nil,0);
  193.       end;
  194.     SERVICE_CONTROL_CONTINUE:
  195.       begin
  196.         FServiceStatus.dwCurrentState := SERVICE_RUNNING;
  197.         Inserts[0] := StrPCopy(Text,IntToStr(GetTickCount - FPauseStartTicks));
  198.         LogEvent(EVENTLOG_INFORMATION_TYPE,DEMO1_SERVICE_CONTINUED,@Inserts,1);
  199.       end;
  200.     SERVICE_CONTROL_INTERROGATE:
  201.       begin
  202.         { Will be set after the case statement. }
  203.       end;
  204.   else
  205.     begin
  206.       Inserts[0] := StrPCopy(Text,IntToStr(Code));
  207.       LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_CODE_INVALID,@Inserts,1);
  208.     end;
  209.   end;
  210.   if not SetServiceStatus(FServicStatusHandle,FServiceStatus) then
  211.     begin
  212.       Inserts[0] := StrPCopy(Text,SysErrorMessage(GetLastError));
  213.       LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_UPDATE_STATUS_FAILED,@Inserts,1);
  214.     end;
  215.   if FServiceStatus.dwCurrentState = SERVICE_STOP_PENDING then
  216.     FTerminated := True;
  217. end;
  218. {------------------------------------------------------------------------------}
  219. Procedure DemoServiceMain(NumArgs: DWord; Args: PCharArray); StdCall;
  220. var
  221.   InitialisedOK: Boolean;
  222.   Inserts: array[0..0] of PChar;
  223.   Text: array[0..255] of char;
  224.   BeepDelay: Integer;
  225.  
  226. begin
  227.   BeepDelay := 1000;
  228.   if NumArgs > 1 then
  229.     begin
  230.       { Only try and convert last parameter passed }
  231.       try
  232.         Beepdelay := StrToInt(Args^[NumArgs-1]);
  233.       except
  234.         LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_STARTUP_PARM_INVALID,@Args^[NumArgs-1],1);
  235.       end;
  236.       if (BeepDelay < 500) then
  237.         BeepDelay := 500
  238.       else
  239.         if (BeepDelay > 10000) then
  240.           BeepDelay := 10000;
  241.     end;
  242.   FServicStatusHandle := RegisterServiceCtrlHandler(DemoServiceName,@DemoServiceHandler);
  243.   if FServicStatusHandle <> 0 then
  244.     begin
  245.       FillChar(FServiceStatus,sizeof(TServiceStatus),0);
  246.       With FServiceStatus do
  247.         begin
  248.           dwServiceType := SERVICE_WIN32_OWN_PROCESS;
  249.           dwCurrentState := SERVICE_START_PENDING;
  250.           dwControlsAccepted := SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_PAUSE_CONTINUE;
  251.         end;
  252.       { Set status to pending before we do our initilisation }
  253.       if SetServiceStatus(FServicStatusHandle,FServiceStatus) then
  254.         begin
  255.           { Do initialisation here. If it takes > 1 sec you should call SetServiceStatus }
  256.           { passing wait hints and checkpoints to show progress is being made.           }
  257.           { Simulate time taken to initialise }
  258.           Sleep(1000);
  259.           InitialisedOK := True; { We assume initialisation was OK for this demo !!! }
  260.           if InitialisedOK then
  261.             begin
  262.               FServiceStatus.dwCurrentState := SERVICE_RUNNING;
  263.               if SetServiceStatus(FServicStatusHandle,FServiceStatus) then
  264.                 begin
  265.                   LogEvent(EVENTLOG_INFORMATION_TYPE,DEMO1_SERVICE_STARTED,nil,0);
  266.                   { Main loop of service process }
  267.                   While not FTerminated do
  268.                     begin
  269.                       Sleep(BeepDelay);
  270.                       if not (FServiceStatus.dwCurrentState = SERVICE_PAUSED) then
  271.                         MessageBeep(0);
  272.                     end;
  273.  
  274.                   if FServiceStatus.dwCurrentState = SERVICE_STOP_PENDING then
  275.                     begin
  276.                       { Do clenaup processing here }
  277.                       LogEvent(EVENTLOG_INFORMATION_TYPE,DEMO1_SERVICE_ENDED,nil,0);
  278.                       FServiceStatus.dwCurrentState := SERVICE_STOPPED;
  279.                       if not SetServiceStatus(FServicStatusHandle,FServiceStatus) then
  280.                         begin
  281.                           Inserts[0] := StrPCopy(Text,SysErrorMessage(GetLastError));
  282.                           LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_UPDATE_STATUS_FAILED,@Inserts,1);
  283.                         end;
  284.                     end;
  285.                 end
  286.               else
  287.                 begin
  288.                   Inserts[0] := StrPCopy(Text,IntToStr(GetLastError));
  289.                   LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_UPDATE_STATUS_FAILED,@Inserts,1);
  290.                 end;
  291.             end
  292.           else
  293.             With FServiceStatus do
  294.               begin
  295.                 LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_FAILED,nil,0);
  296.                 dwCurrentState := SERVICE_STOPPED;
  297.                 dwWin32ExitCode := 666; { Set a code to indicate reason for failure }
  298.                 SetServiceStatus(FServicStatusHandle,FServiceStatus);
  299.               end;
  300.         end
  301.       else
  302.         begin
  303.           Inserts[0] := StrPCopy(Text,SysErrorMessage(GetLastError));
  304.           LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_REGHANDLER_FAILED,@Inserts,1);
  305.         end;
  306.     end
  307.   else
  308.     begin
  309.       Inserts[0] := StrPCopy(Text,SysErrorMessage(GetLastError));
  310.       LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_UPDATE_STATUS_FAILED,@Inserts,1);
  311.     end;
  312. end;
  313. {------------------------------------------------------------------------------}
  314. { Main() entry point for program                                               }
  315. var
  316.   Param: ShortString;
  317.   ServiceEntryTable: PServiceTableEntry;
  318.   Inserts: array[0..0] of PChar;
  319.   Text: array[0..255] of Char;
  320.  
  321. begin
  322.   FTerminated := False;
  323.   Param := UpperCase(ParamStr(1));
  324.   if (Param = 'INSTALL') or (Param = 'I')  then
  325.     InstallService
  326.   else
  327.     if (Param = 'UNINSTALL') or (Param = 'U') then
  328.       UninstallService
  329.     else
  330.       if (Param = 'VERSION') or (Param = 'V') then
  331.         DisplayVersion
  332.       else
  333.         if Param = '' then
  334.           begin
  335.             { We should have been called by the SCM, so connect to it. }
  336.             ServiceEntryTable := AllocMem(2*SizeOf(TServiceTableEntry));
  337.             try
  338.               ServiceEntryTable^.lpServiceName:= DemoServiceName;
  339.               ServiceEntryTable^.lpServiceProc:= @DemoServiceMain;
  340.               { The CtrlDispatcher loops round waiting for control requests for    }
  341.               { the service(s) detailed in the ServiceEntryTable array. It will    }
  342.               { not return until the all services in the process terminate (or an  }
  343.               { error has occurred)                                                }
  344.               if not StartServiceCtrlDispatcher(ServiceEntryTable^) then
  345.                 begin
  346.                   Inserts[0] := StrPCopy(Text,IntToStr(GetLastError));
  347.                   LogEvent(EVENTLOG_ERROR_TYPE,DEMO1_SERVICE_STARTDISPATCHER_FAILED,@Inserts,1);
  348.                 end;
  349.             finally
  350.               FreeMem(ServiceEntryTable);
  351.             end;
  352.           end
  353.         else
  354.           DisplaySyntaxOptions;
  355. end.
  356.